
using System;
using System.IO;
using System.Mathematics;

namespace System.Windows.Media.DirectShow.Audio
{
    public class AudioFrame
    {
        private double[] _waveRealOut;
        private double[] _waveImagOut;
        private double[] _amplOut;
        private double[] _waveRealIn;

        private int _frameSize;
        private WaveFormatExtensible waveFormat;

        public AudioFrame(WaveFormatExtensible waveFormat, int frameSize)
        {
            _frameSize = frameSize;
            _waveRealOut = new double[frameSize / waveFormat.BlockAlign];
            _waveImagOut = new double[frameSize / waveFormat.BlockAlign];
            _amplOut = new double[frameSize / waveFormat.BlockAlign];
            _waveRealIn = new double[frameSize / waveFormat.BlockAlign];

            this.waveFormat = waveFormat;
        }

        public int FrameSize
        {
            get { return _frameSize; }
        }

        public FFTResult[] GetFFTFrame(byte[] wave, int offset, int length)
        {
            FFTResult[] r = new FFTResult[this.waveFormat.Channels];

            // Per tutti i canali
            for (int x = 0; x < this.waveFormat.Channels; x++)
            {
                int samples = ProcessChannel(wave, x, (uint)offset, length);
                r[x] = new FFTResult
                {
                    RealOut = _waveRealOut,
                    ImaginaryOut = _waveImagOut,
                    AmplitudeOut = _amplOut,
                    Length = samples,
                };
            }

            return r;
        }

        public void PitchShift(byte[] data, int offset, int length)
        {
            for (int x = 0; x < this.waveFormat.Channels; x++)
            {
                int samples = ProcessChannel(data, x, (uint)offset, length);
                PitchShifter.PitchShift(0.5, samples, waveFormat.SamplesPerSec, _waveRealIn);
            }
        }

        private int ProcessChannel(byte[] wave, int channelIndex, uint offset, int length)
        {
            int cbSize = length;
            int samples = cbSize;
            switch (waveFormat.BitsPerSample)
            {
                case 8:
                    {
                        if (waveFormat.Channels == 1) // mono
                        {
                            samples = cbSize;
                            for (uint i = offset; i < cbSize; ++i)
                            {
                                _waveRealIn[i] = (double)((wave[i] - 128) << 6);// Out = (In-128)*64
                            }
                        }
                        else if (waveFormat.Channels == 2) // stereo
                        {
                            // Stereo has Right+Left channels
                            samples = cbSize >> 1;
                            for (uint i = offset, j = 0; i < samples; ++i, j += 2)
                            {
                                if (channelIndex == 1)
                                    _waveRealIn[i] = (double)((wave[j] - 128) << 6); // Out = (In-128)*64
                                else if (channelIndex == 0)
                                    _waveRealIn[i] = (double)((wave[j + 1] - 128) << 6); // Out = (In-128)*64
                            }
                        }
                    }
                    break;
                case 16:
                    {
                        if (waveFormat.Channels == 1) // mono
                        {
                            samples = cbSize >> 1;
                            for (uint i = offset, j = 0; i < samples; ++i, j += 2)
                            {
                                short val = (short)unchecked(((wave[j + 1] << 8) + wave[j]));
                                _waveRealIn[i] = (double)val;
                            }
                        }
                        else if (waveFormat.Channels == 2) // stereo
                        {
                            // Stereo has Right+Left channels
                            samples = cbSize >> 2;
                            for (uint i = offset, j = 0; i < samples; ++i, j += 4)
                            {
                                short val = 0;
                                if (channelIndex == 1)
                                    val = unchecked((short)((wave[j + 1] << 8) + wave[j])); // right
                                else if (channelIndex == 0)
                                    val = unchecked((short)((wave[j + 3] << 8) + wave[j + 2])); // left
                                _waveRealIn[i] = (double)val;
                            }
                        }
                    }
                    break;
                default:
                    System.Diagnostics.Debug.Assert(false, "Format not supported"); // not supported
                    break;
            }

            uint s = (uint)samples;

            // Trim data with silence because must be power of two
            if (!FFT.IsPowerOfTwo(s))
            {
                uint s2 = FFT.NextPowerOfTwo(s);
                for (uint x = s + 1; x < s2; x++)
                    _waveRealIn[x] = 0;

                s = s2;
            }
            if (s == 0) return 0;

            FFT.Compute(s, _waveRealIn, null, _waveRealOut, _waveImagOut, false);
            FFT.Norm(s, _waveRealOut, _waveImagOut, _amplOut);

            return samples;
        }

       
    }
}
